package sgs.skin;

import sgs.common.utils.DataCache;
import sgs.common.utils.FileUtils;
import sgs.common.utils.Logger;
import sgs.common.utils.VersionEnum;

import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;

public class SkinFileProcessor {
    private static final int CONCURRENCY_LEVEL = Runtime.getRuntime().availableProcessors(); // 并发级别设置为处理器数量
    private static final AtomicInteger TOTAL_FILES = new AtomicInteger(0);
    //    private static final AtomicInteger SUCCESSFUL_MOVES = new AtomicInteger(0);
    private static final AtomicInteger FAILED_MOVES = new AtomicInteger(0);

    public static void reorganizeFiles(String srcDir, String targetDir) {
        // 验证路径有效性
        Path sourceDirectory = Paths.get(srcDir);
        Path targetDirectory = Paths.get(targetDir);
        if (!validatePaths(sourceDirectory, targetDirectory)) {
            return;
        }
        ExecutorService moveFileExecutor = Executors.newFixedThreadPool(CONCURRENCY_LEVEL); // 创建固定大小的线程池

        // 遍历目录下的文件
        try {
            Files.walkFileTree(sourceDirectory, new SimpleFileVisitor<Path>() {
                @Override
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
                    // 只处理图片
                    if (!FileUtils.isImageByExtension(file)) {
                        return FileVisitResult.CONTINUE;
                    }

                    TOTAL_FILES.incrementAndGet();
                    String skinDir = getSkinDirForFile(file.getFileName().toString());
                    // 目标目录
                    Path targetDirectory = Paths.get(targetDir, skinDir);
                    // 确保目标目录存在
                    if (!Files.exists(targetDirectory)) {
                        try {
                            Files.createDirectories(targetDirectory);
                        } catch (IOException e) {
                            Logger.log(Level.SEVERE, "无法创建目标目录: " + targetDirectory, e);
                            return FileVisitResult.TERMINATE; // 中止遍历以避免进一步错误
                        }
                    }

                    // 构建目标文件路径
                    Path targetFile = targetDirectory.resolve(file.getFileName());
//                    Path targetFile = targetDirectory.resolve(sourceDirectory.relativize(file));
                    moveFileExecutor.execute(() -> moveFile(file, targetFile)); // 使用线程池执行文件移动任务

                    return FileVisitResult.CONTINUE;
                }

                private void moveFile(Path source, Path target) {
                    // 移动文件，如果目标文件已存在，则跳过
                    try {
                        Files.move(source, target);
//                        SUCCESSFUL_MOVES.incrementAndGet();
                        Logger.log("Moved file: " + source + " to " + target);
                    } catch (FileAlreadyExistsException e) {
                        FAILED_MOVES.incrementAndGet();
                        Logger.log("File already exists: " + target);
                    } catch (IOException e) {
                        FAILED_MOVES.incrementAndGet();
                        Logger.log(Level.SEVERE, "Error moving file: " + source + " to " + target, e);
//                        throw new RuntimeException(e);
                    }
                }

                /**
                 * 从文件名获取skinDir
                 * @param fileName 文件名
                 * @return 皮肤目录名
                 */
                private String getSkinDirForFile(String fileName) {
                    VersionEnum ver = VersionEnum.getByFileName(fileName);
                    return DataCache.skinNameToDirMap.getOrDefault(DataCache.characterCodeToNameMap.get(ver + "." + getChrctCodeBySkinFile(fileName, ver)), "未分类");
                }

                @Override
                public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                    // 打印当前访问的目录，可选
                    // System.out.println("Directory: " + dir);
                    return FileVisitResult.CONTINUE;
                }
            });
        } catch (IOException e) {
            Logger.log(Level.SEVERE, "文件遍历过程中发生错误", e);
        } finally {
            // 在文件遍历结束后关闭线程池
            moveFileExecutor.shutdown();
            // 等待所有任务完成，最多等待5秒
            try {
                if (!moveFileExecutor.awaitTermination(10, TimeUnit.SECONDS)) {
                    Logger.log("moveFileExecutor线程池未能在5秒内关闭，可能存在未完成的任务");
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                Logger.log(Level.SEVERE, "awaitTermination线程被中断，线程池可能没有完全关闭");
            }
        }
        // 输出结果统计
        Logger.log("文件移动操作完成。总文件数: {0}, 移动失败: {1}", new Object[]{TOTAL_FILES.get(), FAILED_MOVES.get()});

    }

    private static boolean validatePaths(Path sourceDirectory, Path targetDirectory) {
        try {
            if (!sourceDirectory.toFile().exists()) {
                Logger.log("源目录不存在: " + sourceDirectory);
                return false;
            }
            if (sourceDirectory.equals(targetDirectory) || targetDirectory.toFile().exists() && targetDirectory.startsWith(sourceDirectory)) {
                Logger.log("目标目录不能是源目录的子目录或本身: " + targetDirectory);
                return false;
            }
            return true;
        } catch (Exception e) {
            Logger.log(Level.SEVERE, "验证路径时发生错误: " + e.getMessage(), e);
            return false;
        }
    }

    /**
     * 从文件名获取皮肤代码
     *
     * @param filename 文件名
     * @return 皮肤编号
     */
    public static String getChrctCodeBySkinFile(String filename, VersionEnum ver) {
        return getChrctCodeBySkinCode(getSkinCodeBySkinFile(filename), ver);
    }

    /**
     * 从文件名提取皮肤代码
     *
     * @param filename 文件名
     * @return skinCode 皮肤代码
     */
    public static String getSkinCodeBySkinFile(String filename) {
        String skinCode = null;
        // 判断文件只包含数字、字母、点(.)、下划线(_)或英文括号（()）
        if (!filename.matches("^[\\w.()]+$")) {
            return filename.substring(0, filename.lastIndexOf("."));
        }
        VersionEnum ver = VersionEnum.getByFileName(filename);
        switch (ver) {
            case ZN:
            case OL:
                // 例如刘备：ol.100.png
                // 例如刘备：zn.100101.png
                skinCode = filename.split("\\.")[1];
                break;
            case MB:
                skinCode = filename.replaceFirst("mb.", "");
                break;
            default:
                break;
        }

        return skinCode;
    }

    /**
     * 根据皮肤代码和版本枚举获取角色代码。
     * 该方法首先尝试从缓存中获取皮肤代码转换成的角色代码。如果缓存中不存在，则根据不同的版本规则进行转换。
     * ZN版本的皮肤代码格式为ZX0Y，角色代码为X。
     * OL版本的皮肤代码格式为X0Y，角色代码为X。
     * MB版本的皮肤代码格式为char.X_1或X0Y_1，角色代码为X。
     *
     * @param skinCode 皮肤代码
     * @param ver      版本枚举
     * @return 角色代码，如果无法转换则返回空字符串。
     */
    public static String getChrctCodeBySkinCode(String skinCode, VersionEnum ver) {
        // 尝试通过完整的皮肤代码从缓存中获取转换后的角色代码
        String code = DataCache.skinCodeToChrctCodeMap.get(ver + "." + skinCode);
        if (code != null) {
            return code;
        }
        // 根据不同的版本规则进行转换
        switch (ver) {
            case ZN:
                // 例如刘备：100101 -> 001
                code = skinCode.substring(1, skinCode.length() - 2);
                break;
            case OL:
                // 例如刘备：100 -> 1
                code = skinCode.substring(0, skinCode.length() - 2);
                break;
            case MB:
                if (skinCode.contains("char")) {
                    // char.1_1 -> 1
                    code = skinCode.substring(skinCode.indexOf("char") + 5, skinCode.indexOf("_"));
                } else {
                    // 104_1 -> 1
                    code = skinCode.substring(0, skinCode.indexOf("_") - 2);
                }
                break;
            default:
                break;
        }
        return code;
    }

    @Deprecated
    private static String getChrctCodeByCode(String filename, VersionEnum ver) {
        String code = null;

        switch (ver) {
            case ZN:
                // 例如刘备：100101 -> 001
                code = filename.split(".")[0];
                code = code.substring(1, code.length() - 2);
                break;
            case OL:
                // 例如刘备：ol.100 -> 1
                code = filename.split(".")[1];
                code = code.substring(0, code.length() - 2);
                break;
            case MB:
                if (filename.indexOf("char") > 0) {// mb.char.1_1
                    code = filename.substring(filename.indexOf("char") + 5, filename.indexOf("_"));
                } else {// mb.104_1
                    code = filename.substring(filename.indexOf(".") + 1, filename.indexOf("_") - 2);
                }
                break;
            default:
                break;
        }

        return code;
    }
}